import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

//受限的left join
// 免除手写多个连接代码;
// 限制手写查询的结构,增加可读性;

//flowTO
//  流向
//flowBy
//  流过
//shutdown
//  表示流结束
//从parent向child要求构造数据,而不能从child到parent要求构造数据

//不使用flowto模型,使用收税模型
//第一阶段返回可计算的对象;根据参数决定对象是否是可计算的
//第二阶段计算时,通过字符串作为key获取第一阶段保存的数据

@Deprecated()//by Cxx
public class SqlManualJoin {

    //依赖树
    //查询条件在被join方时,join方没办法使用(left join本来就不能使用,结果集中,右面的表没有的列,统统为null),但它是符合left join的
    //解析sql
    //支持,左表结果集是0时,不再进行下面的查询

    //可以通过这种方式得到代码自身的结构,然后通过结构分析出,编程人员在编译时的真正想法

    //问题:
    //1.如何使返回类型是page
    //2.连接child时,如何提供信息给parent,例如,child不需要id为0的集合,这个信息如果parent知道的话,则可以避免此child的查询调用
    //3.指定从parent获取的连接ids.

    public static final class JoinAble{

        //注册一个joinAble
        private Consumer<JoinAble> doJoin=null;

        private List<JoinAble> child_list;

        //用户在连接时可能的回调
        private Consumer<Map<String,Object>> self_call;

        //用于child获取ids放在in条件中
        private List<Map<String,Object>> res;

        private JoinAble(){

        }

        //不想暴露内部JoinAble,所以把相关上下文信息聚合到一起.
        public interface Context {
            default String getSql(String sql){
                return "";
            }
            default Set<Object> getParentIds(){
                return new HashSet<>();
            }

            //每一行被连接过后的回调
            //result_row 是parent与child连接(合并)之后的行
            void after_merge(Consumer<Map<String,Object>> after_merge);

            void merge(BiConsumer<Map<String,Object>,Map<String,Object>> merge);
        }

        private static final class HoldCallBack{
            BiConsumer<Map<String,Object>,Map<String,Object>> merge;
            Consumer<Map<String,Object>> after_merge;
            private BiConsumer<Map<String,Object>,Map<String,Object>> getMerge(){
                return merge;
            }
            private void setMerge(BiConsumer<Map<String,Object>,Map<String,Object>> merge){
                this.merge=merge;
            }

            private Consumer<Map<String,Object>> getAfterMerge(){
                return after_merge;
            }
            private void setAfterMerge(Consumer<Map<String,Object>> after_merge){
                this.after_merge=after_merge;
            }
        }

        public JoinAble leftJoin(String on_ParentColumn, String on_ChildColumn, Function<Context,List<Map<String,Object>>> joinAble){
            Consumer<JoinAble> child_doJoin=self->{
                var holdCallBack = new HoldCallBack();
                var data = getRes();
                var joinData = joinAble.apply(new Context() {
                    @Override
                    public String getSql(String sql) {
                        return on_ChildColumn+" as "+on_ParentColumn;
                    }
                    @Override
                    public Set<Object> getParentIds() {
                       return data.stream().map(i -> i.get(on_ParentColumn)).filter(Objects::nonNull).collect(Collectors.toSet());
                    }

                    @Override
                    public void after_merge(Consumer<Map<String, Object>> after_merge) {
                        holdCallBack.setAfterMerge(after_merge);
                    }

                    @Override
                    public void merge(BiConsumer<Map<String, Object>, Map<String, Object>> merge) {
                        holdCallBack.setMerge(merge);
                    }
                });

                if (joinData==null){
                    joinData=new ArrayList<>(0);
                }

                self.setRes(joinData);
                final Map<Object, Map<String, Object>> collect = joinData.stream().collect(Collectors.toMap(i -> i.get(on_ChildColumn), Function.identity()));
                //??这里怎么会这样?我们不是已知是否有self_call了吗?
                if(holdCallBack.getAfterMerge()!=null){
                    Consumer<Map<String, Object>> customer = holdCallBack.getAfterMerge();
                    self.setSelf_call(parent_row->{
                        Map<String, Object> child_row = collect.get(parent_row.get(on_ParentColumn));
                        if (child_row != null) {
                            parent_row.putAll(child_row);
                            customer.accept(parent_row);
                        }
                    });
                }else if(holdCallBack.getMerge()!=null){//自行合并
                    self.setSelf_call(parent_row->{
                        Map<String, Object> child_row = collect.get(parent_row.get(on_ParentColumn));
                        if (child_row != null) {
                            holdCallBack.getMerge().accept(child_row,parent_row);
                        }
                    });
                }else{
                    self.setSelf_call(parent_row->{
                        Map<String, Object> x = collect.get(parent_row.get(on_ParentColumn));
                        if (x != null) {
                            parent_row.putAll(x);
                        }
                    });
                }

            };
            var child = new JoinAble();
            child.setDoJoin(child_doJoin);
            child.setChild_list(new ArrayList<>());
            assert child_list != null;
            child_list.add(child);
            return child;
        }

        //两者相同的公共列进行join
        public JoinAble leftJoin(String common_column,Function<Context,List<Map<String,Object>>> joinAble){
            return leftJoin(common_column,common_column,joinAble);
        }

        private static final class Ix{
            public JoinAble self;

            public JoinAble getSelf() {
                return self;
            }

            public void setSelf(JoinAble self) {
                this.self = self;
            }
        }

        public JoinAble leftJoin(String on_ParentColumn, String on_ChildColumn, BiFunction<Context,JoinAble,List<Map<String,Object>>> joinAble) {
            //并不优雅的方式
            var t= new Ix();
            Function<Context,List<Map<String,Object>>> wrapper_join_able=context->{
                return joinAble.apply(context,t.getSelf());
            };
            t.setSelf(leftJoin(on_ParentColumn,on_ChildColumn,wrapper_join_able));
            return t.getSelf();
        }

        public JoinAble leftJoin_n(Consumer<JoinAble> composite){
            composite.accept(this);
            return this;
        }

        private Consumer<Map<String, Object>> getSelf_call() {
            return self_call;
        }

        private void setSelf_call(Consumer<Map<String, Object>> self_call) {
            this.self_call = self_call;
        }

        private List<Map<String, Object>> getRes() {
            return res;
        }

        private void setRes(List<Map<String, Object>> res) {
            this.res = res;
        }

        private List<Map<String,Object>> getData(List<Object> id_column){
            return getRes();
        }

        private void setData(List<Map<String,Object>> data){
            setRes(data);
        }

        private List<JoinAble> getChild_list() {
            return child_list;
        }

        private void setChild_list(List<JoinAble> child_list) {
            this.child_list = child_list;
        }

        private Consumer<JoinAble> getDoJoin() {
            return doJoin;
        }

        private void setDoJoin(Consumer<JoinAble> doJoin) {
            this.doJoin = doJoin;
        }

        public static Jexecute MakeJoinAble(Function<JoinAble,List<Map<String,Object>>> dataSource){

            var root = new JoinAble();
            root.setDoJoin(self->{
                var res1 = dataSource.apply(self);
                if(res1==null){
                    res1=new ArrayList<>();
                }
                self.setRes(res1);
            });
            root.setSelf_call(i->{
                System.out.println("root default self_call");
            });
            root.setChild_list(new ArrayList<>());
            Jexecute jexecute = new Jexecute();
            jexecute.setRoot(root);
            return jexecute;
        }

        /*todo 我们的弱点是,在参数条件属于在child中时,无法作为parent的条件,所以可以把这些条件先用到child中,随后parent通过child返回的id进行条件的限制
        需要分页的时候,我们仍然无法从parent中获取足够的数据,因为child返回的id可能在parent中不存在了,无法通过再次调用child获取id,因为不知道此次需要多少的id
        或者需要调用几次
         */
        public static Jexecute MakeJoinAble(BiFunction<Context,JoinAble,List<Map<String,Object>>> dataSource){
            //暂时禁止使用此函数
            Objects.requireNonNull(null);
            var root = new JoinAble();
            root.setDoJoin(self->{
                //todo 从这里开始
                var res1 = dataSource.apply(null, self);
                if(res1==null){
                    res1=new ArrayList<>();
                }
                self.setRes(res1);
            });
            if(root.getSelf_call()==null){
                root.setSelf_call(i->{});
            }
            root.setChild_list(new ArrayList<>());
            Jexecute jexecute = new Jexecute();
            jexecute.setRoot(root);
            return jexecute;
        }

        private static List<Map<String,Object>> execute(JoinAble root){
            Queue<JoinAble> queue=new LinkedList<>();
            Queue<JoinAble> queue2=new LinkedList<>();
            queue.add(root);
            JoinAble item=null;
            while (!queue.isEmpty()){
                item=queue.peek();
                item.getDoJoin().accept(item);
                if(!item.getRes().isEmpty()) {
                    queue.addAll(item.getChild_list());
                    queue2.add(item);//只有自身有数据才参与之后的join
                }
                queue.remove();
            }
            //移除root
            if(!queue2.isEmpty()){
                queue2.remove();
            }
            if(queue2.isEmpty()){
                return root.getRes();
            }
            //策略提取抽象
            //todo begin
            List<JoinAble> joinAbles = queue2.stream().toList();
            Consumer<Map<String, Object>> root_self_call = root.getSelf_call();
            BiConsumer<Map<String,Object>,List<JoinAble>> mx=null;
            if(joinAbles.size()==1){
                mx=JoinAble::m1;
            }else if(joinAbles.size()==2){
                mx=JoinAble::m2;
            }else if(joinAbles.size()==3){
                mx=JoinAble::m3;
            } else if(joinAbles.size()==4){
                mx=JoinAble::m4;
            }else{
                mx=(item2,list)->{
                    for(var joina:list){
                        joina.getSelf_call().accept(item2);
                    }
                };
            }
            for(var itemx:root.getRes()){
               mx.accept(itemx,joinAbles);
               root_self_call.accept(itemx);
            }
            //todo end
            return root.getRes();
        }

        private static void m1(Map<String,Object> item,List<JoinAble> list){
            assert list.size()==1;
            list.get(0).getSelf_call().accept(item);
        }
        private static void m2(Map<String,Object> item,List<JoinAble> list){
            assert list.size()==2;
            list.get(0).getSelf_call().accept(item);
            list.get(1).getSelf_call().accept(item);
        }
        private static void m3(Map<String,Object> item,List<JoinAble> list){
            assert list.size()==3;
            list.get(0).getSelf_call().accept(item);
            list.get(1).getSelf_call().accept(item);
            list.get(2).getSelf_call().accept(item);
        }
        private static void m4(Map<String,Object> item,List<JoinAble> list){
            assert list.size()==4;
            list.get(0).getSelf_call().accept(item);
            list.get(1).getSelf_call().accept(item);
            list.get(2).getSelf_call().accept(item);
            list.get(3).getSelf_call().accept(item);
        }

        public static final class Jexecute{
            JoinAble root;

            public JoinAble getRoot() {
                return root;
            }

            public void setRoot(JoinAble root) {
                this.root = root;
            }

            public List<Map<String, Object>> execute(){
                return JoinAble.execute(root);
            }
        }

    }

    //孩子链接测试
    @Test
    public void test1(){
        List<Map<String, Object>> mapList =JoinAble.MakeJoinAble((self)->{
            List<Map<String,Object>> list = new ArrayList<>();
            list.add(Map.of("id",1,"name","a"));
            list.add(Map.of("id",2,"name","b"));
            list.add(Map.of("id",3,"name","c"));
            list.add(Map.of("id",4,"name","d"));
            list.add(Map.of("id",5,"name","e"));
            list.add(Map.of("id",6,"name","f"));
            for(var i=0;i<list.size();i++){
                HashMap<String, Object> dx = new HashMap<>(list.get(i));
                list.set(i,dx);
            }
            return list;
        }).execute();

        System.out.println(mapList);
    }

    //兄弟链接测试
    @Test
    public void test2() {
        JoinAble.Jexecute jexecute = new JoinAble.Jexecute();
        JoinAble.MakeJoinAble((self) -> {
            List<Map<String, Object>> list = new ArrayList<>();
            list.add(Map.of("id", 1, "name", "a"));
            list.add(Map.of("id", 2, "name", "b"));
            list.add(Map.of("id", 3, "name", "c"));
            list.add(Map.of("id", 4, "name", "d"));
            list.add(Map.of("id", 5, "name", "e"));
            list.add(Map.of("id", 6, "name", "f"));
            for (var i = 0; i < list.size(); i++) {
                HashMap<String, Object> dx = new HashMap<>();
                dx.putAll(list.get(i));
                list.set(i, dx);
            }
            return list;
        });
        List<Map<String, Object>> mapList = JoinAble.execute(jexecute.getRoot());
        System.out.println(mapList);
    }

    //兄弟连接测试
    @Test
    public void test3(){
        List<Map<String, Object>> mapList =JoinAble.MakeJoinAble((self)->{
            List<Map<String,Object>> list = new ArrayList<>();
            list.add(Map.of("id",1,"name","a"));
            list.add(Map.of("id",2,"name","b"));
            list.add(Map.of("id",3,"name","c"));
            list.add(Map.of("id",4,"name","d"));
            list.add(Map.of("id",5,"name","e"));
            list.add(Map.of("id",6,"name","f"));
            for(var i=0;i<list.size();i++){
                HashMap<String, Object> dx = new HashMap<>();
                dx.putAll(list.get(i));
                list.set(i,dx);
            }

            self.leftJoin("id", "id1", ids->{
                String sql = ids.getSql("select * from");
                ArrayList<Map<String,Object>> objects = new ArrayList<>();
                for(var id:ids.getParentIds()){
                    objects.add(Map.of("id1",id,"child1","a"));
                }
                return objects;
            });

            self.leftJoin("id1", "id2", ids->{
                ArrayList<Map<String,Object>> objects = new ArrayList<>();
                for(var id:ids.getParentIds()){
                    objects.add(Map.of("id2",id,"child11","a"));
                }
                return objects;
            });

            //使用重载函数
            self.leftJoin("id",context -> {
                return null;
            });

            self.setSelf_call(i->{
                System.out.println("root当前是:"+i);
            });
            return list;
        }).execute();
        System.out.println(mapList);
    }

    //测试两个参数的左连接
    @Test
    public void test4(){
        List<Map<String, Object>> mapList =JoinAble.MakeJoinAble((self)->{
            List<Map<String,Object>> list = new ArrayList<>();
            list.add(Map.of("id",1,"name","a"));
            list.add(Map.of("id",2,"name","b"));
            list.add(Map.of("id",3,"name","c"));
            list.add(Map.of("id",4,"name","d"));
            list.add(Map.of("id",5,"name","e"));
            list.add(Map.of("id",6,"name","f"));
            for(var i=0;i<list.size();i++){
                HashMap<String, Object> dx = new HashMap<>();
                dx.putAll(list.get(i));
                list.set(i,dx);
            }

            self.leftJoin("id", "id1", ids->{
                String sql = ids.getSql("select * from");
                ArrayList<Map<String,Object>> objects = new ArrayList<>();
                for(var id:ids.getParentIds()){
                    objects.add(Map.of("id1",id,"child1","a"));
                }
                return objects;
            });

            //使用重载函数
            self.leftJoin("id",context -> {
                return null;
            });

            //使用两个参数的左连接
            self.leftJoin("id","id3",(context,self1)->{
                ArrayList<Map<String,Object>> objects = new ArrayList<>();
                context.after_merge(i->{
                    System.out.println("after_merge "+i);
                });
                for(var id:context.getParentIds()){
                    objects.add(Map.of("id3",id,"child1","a"));
                }
                self1.leftJoin("id3",context1 -> {
                   return null;
                });
                return objects;
            });

            self.setSelf_call(i->{
//                System.out.println("root当前是:"+i);
            });
            return list;
        }).execute();
        System.out.println(mapList);
    }

    @Test
    public void testlambda(){
        Consumer<Integer> x=m->{
            var k=5;
        };

    }

}
